package com.mofeng.myshop.until;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.vladsch.flexmark.ext.jekyll.tag.JekyllTagExtension;
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.ext.toc.SimTocExtension;
import com.vladsch.flexmark.ext.toc.TocExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.MutableDataSet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.DigestUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.*;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 工具类
 */
@Slf4j
public class MyUtil {

    /**
     * String
     */
    public static class StringUtil {
        public static boolean isEmpty(String text) {
            return text == null || text.isEmpty();
        }

        public static boolean isEmpty(Object text) {
            return text == null || "".equals(text);
        }

        public static String asStr(Object text) {
            return text == null ? "" : text.toString();
        }

        /**
         * 对邮箱加密
         *
         * @param email 邮箱
         */
        public static String maskEmail(String email) {
            int atIndex = email.indexOf('@');
            if (atIndex <= 4) {
                return email; // 如果邮箱名部分少于或等于4个字符，直接返回
            }
            // 将邮箱的前面部分替换为 *
            return email.substring(0, atIndex - 4) + "****" + email.substring(atIndex - 4);
        }

        /**
         * 手机号加密
         *
         * @param phone 手机号
         */
        public static String maskPhone(String phone) {
            if (phone.length() < 7) {
                return phone; // 如果手机号码长度不足7位，直接返回
            }
            // 将手机号码的中间部分替换为 *
            return phone.substring(0, 3) + "****" + phone.substring(7);
        }

        /**
         * 将各种格式的字符串转换为小驼峰（camelCase）格式。
         * 下划线分隔：如 snake_case。
         * 连字符分隔：如 kebab-case。
         * 大驼峰：如 PascalCase。
         * 全小写或全大写：如 alllowercase 或 ALLUPPERCASE
         *
         * @param input 输入字符串
         * @return 转换后的小驼峰格式字符串
         */
        public static String toCamelCase(String input) {
            if (input == null || input.isEmpty()) {
                return input;
            }

            StringBuilder result = new StringBuilder();
            boolean capitalizeNext = false;

            for (int i = 0; i < input.length(); i++) {
                char c = input.charAt(i);

                // 忽略非字母数字字符，但设置下一个字符为大写
                if (!Character.isLetterOrDigit(c)) {
                    capitalizeNext = true;
                    continue;
                }

                // 如果需要将下一个字符大写，则进行转换
                if (capitalizeNext && result.length() > 0) {
                    result.append(Character.toUpperCase(c));
                    capitalizeNext = false;
                } else {
                    // 否则保持原样添加到结果中，但确保第一个字符是小写的
                    if (result.length() == 0) {
                        result.append(Character.toLowerCase(c));
                    } else {
                        result.append(c);
                    }
                }
            }

            return result.toString();
        }

        /**
         * 将各种格式的字符串转换为大驼峰（Pascal Case）格式。
         * 下划线分隔：如 snake_case。
         * 连字符分隔：如 kebab-case。
         * 小驼峰：如 camelCase。
         * 全小写或全大写：如 alllowercase 或 ALLUPPERCASE
         *
         * @param input 输入字符串
         * @return 转换后的大驼峰格式字符串
         */
        public static String toPascalCase(String input) {
            if (input == null || input.isEmpty()) {
                return input;
            }

            StringBuilder result = new StringBuilder();
            boolean capitalizeNext = true;

            for (int i = 0; i < input.length(); i++) {
                char c = input.charAt(i);

                // 忽略非字母数字字符，但设置下一个字符为大写
                if (!Character.isLetterOrDigit(c)) {
                    capitalizeNext = true;
                    continue;
                }

                // 如果需要将下一个字符大写，则进行转换
                if (capitalizeNext) {
                    result.append(Character.toUpperCase(c));
                    capitalizeNext = false;
                } else {
                    // 否则保持原样添加到结果中
                    result.append(Character.toLowerCase(c));
                }
            }

            // 确保第一个字符总是大写的
            if (result.length() > 0) {
                result.setCharAt(0, Character.toUpperCase(result.charAt(0)));
            }

            return result.toString();
        }

        /**
         * 删除字符串开头的指定前缀（可以选择是否忽略大小写）。
         *
         * @param input      输入字符串
         * @param prefix     要移除的前缀字符串
         * @param ignoreCase 是否忽略大小写进行匹配
         * @return 处理后的字符串
         */
        public static String removePrefix(String input, String prefix, boolean ignoreCase) {
            if (input == null || prefix == null || input.isEmpty() || prefix.isEmpty()) {
                return input;
            }

            // 根据是否忽略大小写来检查前缀
            if ((ignoreCase && input.regionMatches(true, 0, prefix, 0, prefix.length())) || (!ignoreCase && input.startsWith(prefix))) {
                return input.substring(prefix.length());
            }

            return input;
        }
    }

    /**
     * 正则表达式相关
     */
    public static class RegularUtil {
        /**
         * 判断是不是邮箱
         *
         * @param emailAddress
         * @return
         */
        public static boolean isEmailAddresOk(String emailAddress) {
            String regEx = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
            Pattern p = Pattern.compile(regEx);
            Matcher m = p.matcher(emailAddress);
            return m.matches();
        }

        public static boolean isPhoneOk(String phone) {
            return true;
        }
    }

    /**
     * List
     */
    public static class ListUtil {
        public static boolean isEmpty(Collection list) {
            return list == null || list.size() == 0;
        }
    }

    /**
     * Number
     */
    public static class NumberUtil {
        public static Integer asInt(String text) {
            Double aDouble = asDouble(text);
            if (aDouble != null) {
                return aDouble.intValue();
            }
            return null;
        }

        public static Double asDouble(String text) {
            return (StringUtil.isEmpty(text) || !isNum(text)) ? null : Double.parseDouble(text);
        }

        public static boolean isNum(String text) {
            return text.matches("-?\\d+(\\.\\d+)?");
        }

        public static Long asLong(String text) {
            return (StringUtil.isEmpty(text) || !isNum(text)) ? null : Long.parseLong(text);
        }

        /**
         * 转为16进制(默认0x前缀)
         *
         * @param text 转换的进制
         * @return .
         */
        public static String toHex(Object text) {
            return toHex("0x", text, "");
        }

        /**
         * 转为16进制
         *
         * @param prefix 前缀
         * @param text   文本
         * @param suffix 后缀
         * @return .
         */
        public static String toHex(String prefix, Object text, String suffix) {
            if (text == null) return null;
            if (prefix == null) prefix = "";
            if (suffix == null) suffix = "";
            if (text instanceof String || text instanceof Integer || text instanceof Long || text instanceof Byte || text instanceof Short) {
                return String.format((prefix + "%02X" + suffix), Long.parseLong(text.toString()));
            } else {
                throw new RuntimeException("不支持 " + text.getClass().getTypeName() + " 转换为16进制");
            }
        }

        /**
         * 转换成十进制
         *
         * @param hexStr 十进制
         * @return 0
         */
        public static int hexToDecimal(String hexStr) {
            if (hexStr.contains("0x")) {
                hexStr = hexStr.replace("0x", "");
            }
            if (hexStr.contains("0X")) {
                hexStr = hexStr.replace("0X", "");
            }
            return Integer.parseInt(hexStr, 16);
        }

        /**
         * 将整型数字格式化为每4位空格的二进制字符串
         *
         * @param number 要转换的整型数字
         * @return 格式化后的二进制字符串（每4位一个空格）
         */
        public static String formatBinaryWithSpaces(int number) {
            // 1. 调用Integer.toBinaryString()将整型转为二进制字符串（无前导零）
            // 2. 将得到的二进制字符串交给formatBinary方法进行格式化处理
            return formatBinary(Integer.toBinaryString(number), 64);
        }

        /**
         * 将长整型数字格式化为每4位空格的二进制字符串
         *
         * @param number 要转换的长整型数字
         * @return 格式化后的二进制字符串（每4位一个空格）
         */
        public static String formatBinaryWithSpaces(long number) {
            // 1. 调用Long.toBinaryString()将长整型转为二进制字符串（无前导零）
            // 2. 将得到的二进制字符串交给formatBinary方法进行格式化处理
            return formatBinary(Long.toBinaryString(number), 64);
        }

        /**
         * 将二进制字符串格式化为指定长度且每4位空格的格式
         *
         * @param binaryStr 原始二进制字符串
         * @param width     指定的输出长度（位数）
         * @param fixed     指定的分隔长度（位数）
         * @return 格式化后的二进制字符串（固定长度，每4位一个空格）
         */
        public static String formatBinary(String binaryStr, int width, int fixed) {
            // 1. 使用String.format()将二进制字符串补全到指定长度
            //    "%" + width + "s" 创建格式字符串（如"%16s"）
            //    replace(' ', '0') 将补全的空格替换为零
            String binaryStrFixed = String.format("%" + width + "s", binaryStr).replace(' ', '0');

            // 2. 对固定长度的二进制字符串进行每4位加空格处理
            return binaryStrFixed.replaceAll("(.{" + fixed + "})", "$1 ").trim();
        }

        /**
         * 将二进制字符串格式化为指定长度且每4位空格的格式
         *
         * @param binaryStr 原始二进制字符串
         * @param width     指定的输出长度（位数）
         * @return 格式化后的二进制字符串（固定长度，每4位一个空格）
         */
        public static String formatBinary(String binaryStr, int width) {
            return formatBinary(binaryStr, width, 8);
        }
    }

    /**
     * Double
     */
    public static class DoubleUtil {
        /**
         * 格式化double  默认保留两位小数 返回字符串格式
         *
         * @param data 数据
         */
        public static String doubleFormatToStr(double data) {
            return doubleFormatToStr(data, 2);
        }

        /**
         * 格式化double  默认保留两位小数 返回字符串格式
         *
         * @param data 数据
         */
        public static String doubleFormatToStr(double data, int count) {
            return String.format("%." + count + "f", data);
        }

        /**
         * 格式化double  默认保留两位小数
         *
         * @param data 数据
         */
        public static double doubleFormat(double data) {
            return Double.parseDouble(decimalFormat(2).format(data));
        }

        /**
         * 格式化double  默认保留两位小数
         *
         * @param data  数据
         * @param count 保留位数
         */
        public static double doubleFormat(double data, int count) {
            return Double.parseDouble(decimalFormat(count).format(data));
        }

        /**
         * 格式化double。保留多少位小数 默认保留0位
         */
        public static DecimalFormat decimalFormat(int count) {
            if (count < 0) {
                count = 0;
            }
            StringBuilder stringBuilder = new StringBuilder("#0.");
            for (int i = 0; i < count; i++) {
//            stringBuilder.append("0");
                stringBuilder.append("#");
            }
            return new DecimalFormat(stringBuilder.toString());//保留十三位小数
        }
    }

    /**
     * JSON
     */
    public static class JsonUtil {
        //需要FastJson包

        /**
         * 转换为JSON
         *
         * @param o 数据
         */
        public static JSONObject toJsonObject(Object o) {
            return JSON.parseObject(JSON.toJSONString(o));
        }

        /**
         * 将数据转换为Map
         *
         * @param object 数据
         */
        public static Map<String, Object> getDataMap(Object object) {
            Map<String, Object> map = (Map<String, Object>) JSON.parseObject(object.toString(), Map.class);
            return map;
        }

        /**
         * 转换为Map，将指定的关键字
         *
         * @param object 数据
         * @param keys   关键字
         */
        public static Map<String, Object> getDataMap(Object object, String... keys) {
            for (String key : keys) {
                JSONObject jsonObject = toJsonObject(object);
                Object record = jsonObject.get(key);
                if (record == null) {
                    return null;
                }
                Map<String, Object> map = (Map<String, Object>) JSON.parseObject(record.toString(), Map.class);
                if (map != null) {
                    return map;
                }
            }
            return null;
        }

        /**
         * 将数据转换成List集合
         *
         * @param object 数据
         * @param keys   关键字
         */
        public static List<Map<String, Object>> getDataList(Object object, String... keys) {
            for (String key : keys) {
                JSONObject jsonObject = toJsonObject(object);
                JSONArray records = jsonObject.getJSONArray(key);
                if (records == null) {
                    return null;
                }
                List<Map<String, Object>> maps = (List<Map<String, Object>>) JSONArray.parse(records.toString());
                if (maps != null) {
                    return maps;
                }
            }
            return null;
        }

    }

    /**
     * Date
     */
    public static class DateUtil {
        /**
         * 获取时间戳
         *
         * @param time 字符串时间格式
         */
        public static Long getTimeNum(String time) {
            return getTimeNum(time, "yyyy-MM-dd HH:mm:ss.SSS");
        }

        /**
         * 获取时间戳
         *
         * @param time   时间
         * @param format 时间格式
         */
        public static Long getTimeNum(String time, String format) {
            if (time == null || format == null) {
                return null;
            }
            try {
                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(format);
                LocalDateTime parse = LocalDateTime.parse(time, dateTimeFormatter);
                return parse.toInstant(ZoneOffset.UTC).toEpochMilli();
                //ZonedDateTime zonedDateTime = parse.atZone(ZoneId.of("Asia/Shanghai"));
                //return zonedDateTime.toEpochSecond();
            } catch (Exception e) {
                ErrorUtil.printError(e);
                throw new RuntimeException("时间解析异常");
            }
//        try {
//            return new SimpleDateFormat(format).parse(time).getTime();
//        } catch (ParseException e) {
//            ErrorUtil.printError(e);
//            throw new RuntimeException("时间解析异常");
//        }
        }

        /**
         * 将时间戳转换成默认格式的字符串
         *
         * @param time 时间戳
         */
        public static String getTimeStr(Long time) {
            return getTimeStr(time, "yyyy-MM-dd HH:mm:ss.SSS");
        }

        /**
         * 获取时间指定格式的字符
         *
         * @param time   时间戳
         * @param format 时间戳格式
         */
        public static String getTimeStr(Long time, String format) {
            if (time == null || format == null) {
                return null;
            }
            try {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
                simpleDateFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
                //simpleDateFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("Europe/London")));
                //simpleDateFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("America/New_York")));
                return simpleDateFormat.format(time);
//            return new SimpleDateFormat(format).format(time);
            } catch (Exception e) {
                ErrorUtil.printError(e);
                throw new RuntimeException("时间戳解析异常");
            }
        }

        public static Date getNowDate() {
            //设置属性值
            Calendar calendar = Calendar.getInstance();
            // 获取本地时间
            return calendar.getTime();
        }
    }

    /**
     * ===Map====
     */
    public static class MapUtil {
        /**
         * Map<String,List>格式，添加数据，
         *
         * @param map   数据
         * @param key   关键字
         * @param value 值
         */
        public static void mapAppend(Map<String, List> map, String key, Object value) {
            List list = map.computeIfAbsent(key, k -> new ArrayList<>());
            list.add(value);
        }

        /**
         * Map<String,List>格式，添加数据，
         *
         * @param map   数据
         * @param key   关键字
         * @param value 值
         */
        public static void mapAppend(Map<String, List<String[]>> map, String key, String[] value) {
            List list = map.computeIfAbsent(key, k -> new ArrayList<>());
            list.add(value);
        }

        /**
         * Map<String,List>格式，添加数据，
         *
         * @param map   数据
         * @param key   关键字
         * @param value 值
         */
        public static void mapAppend(Map<String, List<String>> map, String key, String value) {
            List list = map.computeIfAbsent(key, k -> new ArrayList<>());
            list.add(value);
        }
    }

    /**
     * =File文件相关=
     */
    public static class FileUtil {
        /**
         * 刪除此文件夹下所有文件(默认删除此文件夹)
         *
         * @param filePath 路径
         */
        public static void cleanDirectory(String filePath) {
            cleanDirectory(filePath, true);
        }

        /**
         * 刪除此文件夹下所有文件
         *
         * @param filePath 文件路径
         * @param flag     是否删除此文件夹
         */
        public static void cleanDirectory(String filePath, boolean flag) {
            File file = new File(filePath);
            if (file.exists()) {
                File[] files = file.listFiles();
                if (files != null) {
                    for (File fileDemo : files) {
                        if (fileDemo.isDirectory()) {
                            cleanDirectory(fileDemo.getPath());
                        }
                        fileDemo.delete();
                    }
                }
            }
            if (flag) {
                file.delete();
            }
        }

        /**
         * 检查文件夹是否存在，不存在则创建
         *
         * @param filePath 文件或者文件夹路径
         */
        public static void checkAndCreateDirectory(String filePath) {
            if (filePath.contains(".")) {
                String splitStr = "";
                if (filePath.contains("/")) {
                    splitStr = "/";
                } else if (filePath.contains("\\\\") || filePath.contains("\\")) {
                    splitStr = "\\\\";
                } else {
                    splitStr = File.separator;
                }
                String[] split = filePath.split(splitStr);
                String fileName = split[split.length - 1];
                String path = filePath.replace(fileName, "");
                createDirectory(path);
            } else {
                createDirectory(filePath);
            }
        }

        /**
         * 创建文件夹
         *
         * @param filePath 文件夹路径
         */
        private static void createDirectory(String filePath) {
            File file = new File(filePath);
            if (!file.exists()) {
                file.mkdirs();
            }
        }

        /**
         * 复制文件
         *
         * @param source      源文件
         * @param destination 复制文件
         */
        public static void copyFile(String source, String destination) {
            // 使用 try-with-resources 语句来自动关闭流，避免手动关闭
            try (InputStream in = new FileInputStream(source);  // 创建一个输入流来读取源文件
                 OutputStream out = new FileOutputStream(destination)) {  // 创建一个输出流来写入目标文件

                // 创建一个字节数组作为缓冲区，用于存放读取的数据块
                byte[] buffer = new byte[1024];  // 1024 是每次读取和写入的字节大小，可以根据需要调整
                int length;  // 用于存储每次从输入流读取的字节数

                // 通过循环读取输入流中的内容，直到文件读取完毕
                while ((length = in.read(buffer)) > 0) {  // 当 read() 返回值大于 0 时，表示还有数据可读
                    // 将读取到的数据写入到输出流
                    out.write(buffer, 0, length);  // 将缓冲区中的数据写入目标文件
                }
                // 捕获可能发生的 I/O 异常并输出错误信息
            } catch (IOException e) {
                // 当发生异常时，输出详细的错误信息
                log.error("文件复制失败: " + e.getMessage());
            }
        }

        /**
         * multipartFile转换成File
         *
         * @param multipartFile 1
         * @return File
         * @throws IOException 1
         */
        public static File multipartFile2File(MultipartFile multipartFile) throws IOException {
            String path = File.separator + "item";
            checkAndCreateDirectory(path);
            File file = new File(path + File.separator + "item");
            if (!file.exists()) {
                boolean newFile = file.createNewFile();
            }
            FileCopyUtils.copy(multipartFile.getBytes(), file);
            cleanDirectory(path, true);
            return file;
        }

        public static String getFileType(MultipartFile file, String defaultType) throws IOException {
            String fileType = getFileType(file.getInputStream());
            if (fileType == null) {
                String fileName = file.getName();
                if (fileName.contains(".")) {
                    int dotIndex = fileName.lastIndexOf('.');
                    if (dotIndex > 0 && dotIndex < fileName.length() - 1) {
                        fileType = fileName.substring(dotIndex + 1);
                    }
                } else {
                    fileType = defaultType;
                }
            }
            return fileType;
        }

        public static String getFileType(File file, String defaultType) throws IOException {
            String fileType = getFileType(file);
            if (fileType == null) {
                fileType = defaultType;
            }
            return fileType;
        }

        public static String getFileType(File file) throws IOException {
            FileInputStream fileInputStream = new FileInputStream(file);
            String fileType = getFileType(fileInputStream);
            fileInputStream.close();
            return fileType;
        }

        public static String getFileType(InputStream inputStream) {
            String fileType = null;
            try {
                // 读取文件开头的几个字节
                byte[] header = new byte[10];
                int read = inputStream.read(header);
                inputStream.close();
                // 根据文件开头的字节判断文件类型
                if (isPDF(header)) {
                    fileType = "pdf";
                } else if (isJPEG(header)) {
                    fileType = "jpg";
                } else if (isPNG(header)) {
                    fileType = "png";
                } else if (isGIF(header)) {
                    fileType = "gif";
                } else if (isTIFF(header)) {
                    fileType = "tiff";
                } else if (isBMP(header)) {
                    fileType = "bmp";
                } else if (isMP3(header)) {
                    fileType = "mp3";
                } else if (isWAV(header)) {
                    fileType = "wav";
                } else if (isOGG(header)) {
                    fileType = "ogg";
                } else if (isDOC(header)) {
                    fileType = "doc";
                } else if (isXLS(header)) {
                    fileType = "xls";
                } else if (isPPT(header)) {
                    fileType = "ppt";
                } else if (isTXT(header)) {
                    fileType = "txt";
                }
                // 在这里可以继续添加其他文件类型的判断条件
            } catch (IOException e) {
                throw new RuntimeException("类型获取失败");
            }
            return fileType;

//            JPEG (jpg)，文件头：FFD8FF
//            PNG (png)，文件头：89504E47
//            GIF (gif)，文件头：47494638
//            TIFF (tif)，文件头：49492A00
//            Windows Bitmap (bmp)，文件头：424D
//            CAD (dwg)，文件头：41433130
//            Adobe Photoshop (psd)，文件头：38425053
//            Rich Text Format (rtf)，文件头：7B5C727466
//            XML (xml)，文件头：3C3F786D6C
//            HTML (html)，文件头：68746D6C3E
//            Email [thorough only] (eml)，文件头：44656C69766572792D646174653A
//            Outlook Express (dbx)，文件头：CFAD12FEC5FD746F
//            Outlook (pst)，文件头：2142444E
//            MS Word/Excel (xls.or.doc)，文件头：D0CF11E0
//            MS Access (mdb)，文件头：5374616E64617264204A
//            WordPerfect (wpd)，文件头：FF575043
//            Postscript (eps.or.ps)，文件头：252150532D41646F6265
//            Adobe Acrobat (pdf)，文件头：255044462D312E
//            Quicken (qdf)，文件头：AC9EBD8F
//            Windows Password (pwl)，文件头：E3828596
//            ZIP Archive (zip)，文件头：504B0304
//            RAR Archive (rar)，文件头：52617221
//            Wave (wav)，文件头：57415645
//            AVI (avi)，文件头：41564920
//            Real Audio (ram)，文件头：2E7261FD
//            Real Media (rm)，文件头：2E524D46
//            MPEG (mpg)，文件头：000001BA
//            MPEG (mpg)，文件头：000001B3
//            Quicktime (mov)，文件头：6D6F6F76
//            Windows Media (asf)，文件头：3026B2758E66CF11
//            MIDI (mid)，文件头：4D546864
        }

        // 判断文件是否为PDF格式
        private static boolean isPDF(byte[] header) {
            return header[0] == 0x25 && header[1] == 0x50 && header[2] == 0x44 && header[3] == 0x46;
        }

        // 判断文件是否为JPEG格式
        private static boolean isJPEG(byte[] header) {
            //
            return header[0] == (byte) 0xFF && header[1] == (byte) 0xD8 && header[2] == (byte) 0xFF && header[3] == (byte) 0xE0;
        }

        // 判断文件是否为PNG格式
        private static boolean isPNG(byte[] header) {
            return header[0] == (byte) 0x89 && header[1] == (byte) 0x50 && header[2] == (byte) 0x4E && header[3] == (byte) 0x47;
        }

        // 判断文件是否为GIF格式
        private static boolean isGIF(byte[] header) {
//            return header[0] == 'G' && header[1] == 'I' && header[2] == 'F' && header[3] == '8';
            return header[0] == (byte) 0x47 && header[1] == (byte) 0x49 && header[2] == (byte) 0x46;
        }

        // 判断文件是否为TIFF格式
        private static boolean isTIFF(byte[] header) {
            return header[0] == 'I' && header[1] == 'I' && header[2] == 0x2A && header[3] == 0x00;
        }

        // 判断文件是否为BMP格式
        private static boolean isBMP(byte[] header) {
            return header[0] == 'B' && header[1] == 'M';
        }

        // 判断文件是否为MP3格式
        private static boolean isMP3(byte[] header) {
            return header[0] == (byte) 0xFF && header[1] == (byte) 0xFB;
        }

        // 判断文件是否为WAV格式
        private static boolean isWAV(byte[] header) {
            return header[0] == 'R' && header[1] == 'I' && header[2] == 'F' && header[3] == 'F';
        }

        // 判断文件是否为OGG格式
        private static boolean isOGG(byte[] header) {
            return header[0] == 'O' && header[1] == 'g' && header[2] == 'g' && header[3] == 'S';
        }

        // 判断文件是否为TXT格式
        private static boolean isTXT(byte[] header) {
            // TXT 文件没有特定的魔法数字，这里简单判断文件开头是否为纯文本内容
            for (byte b : header) {
                if (b < 32 && b != 9 && b != 10 && b != 13) {
                    return false;
                }
            }
            return true;
        }

        // 判断文件是否为DOC格式
        private static boolean isDOC(byte[] header) {
            // DOC 文件的魔法数字为 "D0 CF 11 E0 A1 B1 1A E1"
            return header[0] == (byte) 0xD0 && header[1] == (byte) 0xCF && header[2] == (byte) 0x11 && header[3] == (byte) 0xE0 && header[4] == (byte) 0xA1 && header[5] == (byte) 0xB1 && header[6] == (byte) 0x1A && header[7] == (byte) 0xE1;
        }

        // 判断文件是否为XLS格式
        private static boolean isXLS(byte[] header) {
            // XLS 文件的魔法数字为 "D0 CF 11 E0 A1 B1 1A E1"
            return header[0] == (byte) 0xD0 && header[1] == (byte) 0xCF && header[2] == (byte) 0x11 && header[3] == (byte) 0xE0 && header[4] == (byte) 0xA1 && header[5] == (byte) 0xB1 && header[6] == (byte) 0x1A && header[7] == (byte) 0xE1;
        }

        // 判断文件是否为PPT格式
        private static boolean isPPT(byte[] header) {
            // PPT 文件的魔法数字为 "D0 CF 11 E0 A1 B1 1A E1"
            return header[0] == 0x25 && header[1] == 0x50 && header[2] == 0x44 && header[3] == 0x46;
        }
    }

    /**
     * =Error
     */
    public static class ErrorUtil {
        public static void printError(Exception e) {
            log.error(" 异常类型：{},详细信息：", e.getClass(), e);
        }

        public static void printError(Exception e, String eTitle) {
            log.error(eTitle + "：{}, 异常类型：{},详细信息：", e.getMessage(), e.getClass(), e);
        }

        public static void printError(Exception e, String eTitle, String otherMessage) {
            log.error(otherMessage);
            printError(e, eTitle);
        }

        public static void printError(Throwable t) {
            log.error("出现异常：{}，异常类型：{}, 详细信息：", t.getMessage(), t.getClass(), t);
        }

        public static void printInfo(Exception e, String eTitle) {
            log.info(eTitle + "：{}, 异常类型：{},详细信息：", e.getMessage(), e.getClass(), e);
        }

        public static void printInfo(Exception e, String eTitle, String otherMessage) {
            log.info(otherMessage);
            printError(e, eTitle);
        }

        public static void printInfo(Throwable t) {
            log.info("出现异常：{}，异常类型：{}, 详细信息：", t.getMessage(), t.getClass(), t);
        }

        public static RuntimeException getException() {
            return getException("出现错误", true);
        }

        public static RuntimeException getException(String msg) {
            return getException(msg, false);
        }

        public static RuntimeException getException(String msg, boolean printLog) {
            if (printLog) {
                log.error(msg);
            }
            return new RuntimeException(msg);
        }

        public static String getExceptionStr(Exception e) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            e.printStackTrace(pw);
            return sw.toString();
        }

        public static String getExceptionStr(String eTitle, Exception e) {
            return new StringBuilder(eTitle).append("：{").append(e.getMessage()).append("}, 异常类型：{").append(e.getClass()).append("},详细信息：").append(getExceptionStr(e)).toString();
        }
    }

    /**
     * ===CopyBean========
     */
    public static class CopyBeanUtils {
        /**
         * 复制对象内的所有值，到另一个对象的相同属性名中（同一类的）
         * 思路：
         * 1. 将from的属性遍历，先判断其是否允许复制，然后判断其值为不为空
         * 2. 将允许复制的且值不为空的挑出来，将其值复制到to对象
         *
         * @param to      值 复制 给 此类
         * @param from    值 来自 此类
         * @param exclude 排除一些特殊参数，不复制
         */
        public static void copyObjectSameClass(Object to, Object from, String[] exclude) {
            copyObjectNotNullSameClass(to, from, exclude, false, false);
        }

        /**
         * 复制对象内的所有非空值，到另一个对象的相同属性名中（同一类的）
         * 思路：
         * 1. 将from的属性遍历，先判断其是否允许复制，然后判断其值为不为空
         * 2. 将允许复制的且值不为空的挑出来，将其值复制到to对象
         *
         * @param to      值 复制 给 此类
         * @param from    值 来自 此类
         * @param exclude 排除一些特殊参数，不复制
         */
        public static void copyObjectNotNullSameClass(Object to, Object from, String[] exclude) {
            copyObjectNotNullSameClass(to, from, exclude, false, true);
        }

        /**
         * 复制对象内的所有非空值，到另一个对象的相同属性名中（同一类的）
         * 思路：
         * 1. 将from的属性遍历，先判断其是否允许复制，然后判断其值为不为空
         * 2. 将允许复制的且值不为空的挑出来，将其值复制到to对象
         *
         * @param to       值 复制 给 此类
         * @param from     值 来自 此类
         * @param exclude  排除一些特殊参数，不复制
         * @param hasSuper 是否复制父类
         */
        public static void copyObjectNotNullSameClass(Object to, Object from, String[] exclude, boolean hasSuper, boolean canNull) {
            String name = "";
            try {
                //获取字节码文件
                Class<?> fromClass = from.getClass();
                Class<?> toClass = to.getClass();

                //获取其所有属性
                List<Field> fromDeclaredFields = null;
                List<Field> toDeclaredFields = null;
                if (hasSuper) {
                    fromDeclaredFields = getObjectAllFields(fromClass);
                    toDeclaredFields = getObjectAllFields(toClass);
                } else {
                    fromDeclaredFields = Arrays.asList(fromClass.getDeclaredFields());
                }
                //遍历属性
                for (Field fromField : fromDeclaredFields) {
                    fromField.setAccessible(true);//赋予权限
                    name = fromField.getName();//获取其属性名
                    Object value = fromField.get(from);//从from对象获取其属性值
                    if (canNull && value == null) {//排除空值
                        continue;
                    }
                    Field toField = null;//从to对象获取值
                    try {
                        if (hasSuper) {
                            toField = getObjectFieldByName(name, toDeclaredFields);
                        } else {
                            toField = toClass.getDeclaredField(name);
                        }
                    } catch (NoSuchFieldException e) {
//                        ErrorUtil.printWarring(e, "找不到此属性 " + name);
                        continue;
                    }
                    if (isAll(name, exclude)) {//判断其属性名是否允许复制，且其属性值是否为空
                        toField.setAccessible(true);
                        toField.set(to, value);//将from对象的值复制给to对象
                    }
                }
            } catch (IllegalAccessException e) {
                ErrorUtil.printError(e, name + " 获取值发生错误");
            }
        }

        /**
         * 复制对象内的值，到另一个对象的相同属性名中（非同一类的）
         * 思路：
         * 1. 将from的属性遍历，先判断其是否允许复制，然后判断其值为不为空
         * 2. 将允许复制的且值不为空的挑出来，将其值复制到to对象
         *
         * @param to      值 复制 给 此类
         * @param from    值 来自 此类
         * @param exclude 排除一些特殊参数，不复制
         */
        public static void copyObjectNotSameClass(Object to, Object from, String[] exclude) {
            copyObjectNotNullNotSameClass(to, from, exclude, false, true);
        }

        /**
         * 复制对象内的非空值，到另一个对象的相同属性名中（非同一类的）
         * 思路：
         * 1. 将from的属性遍历，先判断其是否允许复制，然后判断其值为不为空
         * 2. 将允许复制的且值不为空的挑出来，将其值复制到to对象
         *
         * @param to      值 复制 给 此类
         * @param from    值 来自 此类
         * @param exclude 排除一些特殊参数，不复制
         */
        public static void copyObjectNotNullNotSameClass(Object to, Object from, String[] exclude) {
            copyObjectNotNullNotSameClass(to, from, exclude, false, false);
        }

        /**
         * 复制对象内的非空值，到另一个对象的相同属性名中（非同一类的）
         * 思路：
         * 1. 将from的属性遍历，先判断其是否允许复制，然后判断其值为不为空
         * 2. 将允许复制的且值不为空的挑出来，将其值复制到to对象
         *
         * @param to      值 复制 给 此类
         * @param from    值 来自 此类
         * @param exclude 排除一些特殊参数，不复制
         */
        public static void copyObjectNotNullNotSameClass(Object to, Object from, String[] exclude, boolean hasSuper, boolean canNull) {
            String name = "";
            try {
                //获取字节码文件
                Class<?> fromClass = from.getClass();
                Class<?> toClass = to.getClass();

                //获取其所有属性
                List<Field> fromDeclaredFields = null;
                List<Field> toDeclaredFields = null;
                if (hasSuper) {
                    fromDeclaredFields = getObjectAllFields(fromClass);
                    toDeclaredFields = getObjectAllFields(toClass);
                } else {
                    fromDeclaredFields = Arrays.asList(fromClass.getDeclaredFields());
                }

                //遍历属性
                for (Field fromField : fromDeclaredFields) {
                    fromField.setAccessible(true);//赋予权限
                    name = fromField.getName();//获取其属性名
                    Object value = fromField.get(from);//从from对象获取其属性值
                    if (isAll(name, exclude) && (canNull || value != null)) {//判断其属性名是否允许复制，且其属性值是否为空
                        Field toField = null;//从to对象获取值
                        try {
                            if (hasSuper) {
                                toField = getObjectFieldByName(name, toDeclaredFields);
                            } else {
                                toField = toClass.getDeclaredField(name);
                            }
                        } catch (NoSuchFieldException e) {
//                        ErrorUtil.printWarring(e, "找不到此属性 " + name);
                            continue;
                        }
                        toField.setAccessible(true);
                        try {
                            toField.set(to, value);//将from对象的值复制给to对象
                        } catch (Exception e) {
                            ErrorUtil.printError(e, name + " 属性的类型不匹配");
                        }
                    }
                }
            } catch (IllegalAccessException e) {
                ErrorUtil.printError(e, name + " 获取值发生错误");
            }
        }

        /**
         * 找到此属性名的属性，找不到报错
         *
         * @param name           名称
         * @param declaredFields 属性列表
         * @throws NoSuchFieldException 1
         */
        private static Field getObjectFieldByName(String name, List<Field> declaredFields) throws NoSuchFieldException {
            for (Field declaredField : declaredFields) {
                if (declaredField.getName().equals(name)) {
                    return declaredField;
                }
            }
            throw new NoSuchFieldException();
        }

        /**
         * 获取其所有属性及其父类的属性
         *
         * @param clazz 字节码
         * @return 0
         */
        private static List<Field> getObjectAllFields(Class<?> clazz) {
            //获取其属性
            Field[] declaredFields = clazz.getDeclaredFields();
            ArrayList<Field> fields = Arrays.stream(declaredFields).collect(Collectors.toCollection(ArrayList::new));
            //获取其父类属性
            Class<?> superclass = clazz.getSuperclass();
            if (superclass != null) {
                fields.addAll(getObjectAllFields(superclass));
            }
            return fields;
        }

        /**
         * 此值是否排除
         *
         * @param key     关键字
         * @param exclude 排除
         * @return 0
         */
        private static boolean isAll(String key, String[] exclude) {
            if (exclude != null && exclude.length > 0) {
                for (String s : exclude) {
                    if (s.equals(key)) {
                        return false;
                    }
                }
            }
            return true;
        }
    }

    /**
     * ===反射========
     */
    public static class ClazzUtils {
        public static Object getValue(Class clazz, String name, Object object) throws Exception {
            Field field = clazz.getDeclaredField(name);
            field.setAccessible(true);
            return field.get(object);
        }

        public static Object getValue(Field field, Object object) throws Exception {
            field.setAccessible(true);
            return field.get(object);
        }

        public static <T> T getValue(Class clazz, String name, Object object, Class<T> resultClass) throws Exception {
            Field field = clazz.getDeclaredField(name);
            field.setAccessible(true);
            return (T) getValue(clazz, name, object);
        }
    }

    /**
     * ===系统相关========
     */
    public static class SystemUtils {
        /**
         * 获取当前项目的根目录
         *
         * @return 系统根目录
         */
        public static String getRootDir() {
            return System.getProperty("user.dir");
        }
    }

    /**
     * ===SQL相关========
     */
    public static class SqlUtils {
        /**
         * sql查询 模糊查询关键字过滤
         *
         * @param sql SQL
         * @return 过滤后的语句
         */
        public static String searchSqlChar(String sql) {
            String result = null;
            if (sql != null) {
                result = sql.replaceAll("_", "\\\\_");
                result = sql.replaceAll("%", "\\\\%");
                result = result.trim();
            }
            return result;
        }
    }

    /**
     * =MD5=
     */
    public static class MD5Util {

        /**
         * 计算字符串的 MD5 值
         *
         * @param text 要计算 MD5 的字符串
         * @return MD5 值的十六进制字符串表示
         */
        public static String calculateMD5(String text) {
            try {
                // 获取 MessageDigest 实例，指定算法为 MD5
                MessageDigest md = MessageDigest.getInstance("MD5");
                // 更新摘要信息DigestUtils.md5DigestAsHex(
                md.update(text.getBytes());
                // 计算哈希值并转换为十六进制字符串
                byte[] digest = md.digest();
                return convertToHex(digest);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                return null;
            }
        }

        /**
         * 计算文件的 MD5 值
         *
         * @param file 要计算 MD5 的文件
         * @return MD5 值的十六进制字符串表示
         */
        public static String calculateMD5(File file) {
            try (InputStream is = new FileInputStream(file)) {
                return calculateMD5(is);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        /**
         * 计算文件的 MD5 值
         *
         * @param is 要计算 MD5 的文件
         * @return MD5 值的十六进制字符串表示
         */
        public static String calculateMD5(InputStream is) {
            try {
                // 获取 MessageDigest 实例，指定算法为 MD5
                MessageDigest md = MessageDigest.getInstance("MD5");
                // 使用缓冲区读取文件内容，提高效率
                try (BufferedInputStream bis = new BufferedInputStream(is)) {
                    byte[] buffer = new byte[1024];
                    int length;
                    // 逐段读取文件内容并更新摘要信息
                    while ((length = bis.read(buffer)) != -1) {
                        md.update(buffer, 0, length);
                    }
                    // 计算哈希值并转换为十六进制字符串
                    byte[] digest = md.digest();
                    return convertToHex(digest);
                }
            } catch (NoSuchAlgorithmException | IOException e) {
                e.printStackTrace();
                return null;
            }
        }

        /**
         * 将字节数组转换为十六进制字符串表示
         *
         * @param bytes 要转换的字节数组
         * @return 十六进制字符串
         */
        private static String convertToHex(byte[] bytes) {
//            StringBuilder hexString = new StringBuilder();
//            for (byte b : bytes) {
//                // 将每个字节转换为十六进制表示并拼接到字符串中
//                String hex = Integer.toHexString(0xff & b);
//                if (hex.length() == 1) {
//                    hexString.append('0');
//                }
//                hexString.append(hex);
//            }
//            return hexString.toString();
            return DigestUtils.md5DigestAsHex(bytes);
        }

    }

    /**
     * ===Html相关分页相关========
     */
    public static class HtmlUtil {
//        <dependency>
//            <groupId>com.vladsch.flexmark</groupId>
//            <artifactId>flexmark-all</artifactId>
//            <version>0.50.44</version>
//        </dependency>

        /**
         * Markdown转html
         */
        public static String mdToHtml(String content) {
            //转HTML
            MutableDataSet options = new MutableDataSet().set(Parser.EXTENSIONS, Arrays.asList(TablesExtension.create(), JekyllTagExtension.create(), TocExtension.create(), SimTocExtension.create()));
            Parser parser = Parser.builder(options).build();
            HtmlRenderer renderer = HtmlRenderer.builder(options).build();
            Node document = parser.parse(content);
            return renderer.render(document);
//        return content;
        }
    }

    /**
     * ===网络相关========
     */
    public class ServletUtil {
        /**
         * 获取request
         *
         * @return
         */
        public static HttpServletRequest getRequest() {
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            return requestAttributes.getRequest();
        }

        /**
         * 获取response
         *
         * @return
         */
        public static HttpServletResponse getResponse() {
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            return requestAttributes.getResponse();
        }

        public static String getUserIp() {
            return getRequest().getRemoteAddr();
//        return getRequest().getHeader("X-Forwarded-For");
        }

        public static List<String> getAllIpAddress(String domain) {
            try {
                // 获取域名的所有 IP 地址
                domain = domain.trim();
                List<String> result = new ArrayList<>();
                InetAddress[] inetAddresses = InetAddress.getAllByName(domain);
                for (InetAddress inetAddress : inetAddresses) {
                    result.add(inetAddress.getHostAddress());
                }
                return result;
            } catch (Exception e) {
                String errMsg = domain + "，域名无法解析，报错信息: " + e.getMessage();
                log.error(errMsg);
                e.printStackTrace();
                throw new RuntimeException(errMsg, e);
            }
        }

        public static String getIPAddress(String domain) {
            try {
                domain = domain.trim();
                InetAddress inetAddress = InetAddress.getByName(domain);
                return inetAddress.getHostAddress();
            } catch (UnknownHostException e) {
                String errMsg = domain + "，域名无法解析，报错信息: " + e.getMessage();
                log.error(errMsg);
                e.printStackTrace();
                throw new RuntimeException(errMsg, e);
            }
        }
    }

    /**
     * ===图片相关========
     */
    public class ImageUtil {

        /**
         * 读取 PNG 图片并进行缩放处理，最后保存回 PNG 格式。
         *
         * @param inputFile   输入的 PNG 图片文件
         * @param outputFile  输出的 PNG 图片文件
         * @param scaleFactor 缩放比例（0.5 表示将图像缩小一半）
         * @throws IOException 如果读取或写入文件时出现错误
         */
        public static void processPNGImage(File inputFile, File outputFile, double scaleFactor) throws IOException {
            // 1. 读取 PNG 图片
            BufferedImage originalImage = ImageIO.read(inputFile);

            // 2. 计算新的图片尺寸
            int newWidth = (int) (originalImage.getWidth() * scaleFactor);
            int newHeight = (int) (originalImage.getHeight() * scaleFactor);

            // 3. 创建新的图像并绘制原图到新的图像上（缩放）
            Image resizedImage = originalImage.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
            BufferedImage outputImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = outputImage.createGraphics();
            g2d.drawImage(resizedImage, 0, 0, null);
            g2d.dispose();
            // 4. 保存处理后的 PNG 图片
            ImageIO.write(outputImage, "PNG", outputFile);  // 保存为 PNG 格式
        }

        /**
         * 读取 JPEG 图片并进行缩放处理，最后保存为新的 JPEG 文件。
         *
         * @param inputFile   输入的 JPEG 图片文件
         * @param outputFile  输出的处理后 JPEG 图片文件
         * @param scaleFactor 缩放比例（例如：0.5 表示将图像缩小一半）
         * @throws IOException 如果读取或写入文件时出现错误
         */
        public static void processJPEGImage(File inputFile, File outputFile, double scaleFactor) throws IOException {
            // 1. 读取 JPEG 图片
            BufferedImage originalImage = ImageIO.read(inputFile);

            // 2. 计算新的图片尺寸
            int newWidth = (int) (originalImage.getWidth() * scaleFactor);
            int newHeight = (int) (originalImage.getHeight() * scaleFactor);

            // 3. 创建新的图像并绘制原图到新的图像上（缩放）
            Image resizedImage = originalImage.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
            BufferedImage outputImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = outputImage.createGraphics();
            g2d.drawImage(resizedImage, 0, 0, null);
            g2d.dispose();

            // 4. 保存处理后的 JPEG 图片
            ImageIO.write(outputImage, "JPEG", outputFile);  // 保存为 JPEG 格式
        }

        /**
         * 读取 GIF 文件并调整图像大小，最后保存为新 GIF 文件
         *
         * @param inputFile   输入的 GIF 文件
         * @param outputFile  输出的 GIF 文件
         * @param scaleFactor 缩放比例（例如：0.5 表示将图像缩小一半）
         * @throws IOException 如果读取或写入文件时出现错误
         */
        public static void processGIF(File inputFile, File outputFile, double scaleFactor) throws IOException {
            // 使用 ImageIO 读取 GIF 文件
            ImageInputStream inputStream = ImageIO.createImageInputStream(inputFile);
            Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream);
            if (!readers.hasNext()) {
                throw new IOException("No reader found for GIF format");
            }

            // 选择 GIF 图像读取器
            ImageReader reader = readers.next();
            reader.setInput(inputStream);

            // 获取 GIF 图像的帧数（GIF 动画的帧数）
            int frameCount = reader.getNumImages(true);

            // 创建图像写入器
            ImageWriter writer = ImageIO.getImageWritersByFormatName("gif").next();

            // 设置输出流
            try (ImageOutputStream outputStream = ImageIO.createImageOutputStream(outputFile)) {
                writer.setOutput(outputStream);

                // 遍历所有帧并进行处理
                for (int i = 0; i < frameCount; i++) {
                    BufferedImage frame = (BufferedImage) reader.read(i);

                    // 调整图像大小
                    int newWidth = (int) (frame.getWidth() * scaleFactor);
                    int newHeight = (int) (frame.getHeight() * scaleFactor);
                    Image resizedImage = frame.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
                    BufferedImage resizedFrame = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
                    Graphics2D g2d = resizedFrame.createGraphics();
                    g2d.drawImage(resizedImage, 0, 0, null);
                    g2d.dispose();

                    // 写入处理后的帧到输出 GIF
                    writer.write(null, new javax.imageio.IIOImage(resizedFrame, null, null), null);
                }
            }
        }

        /**
         * 读取 WebP 文件并调整图像大小，最后保存为新图像
         *
         * @param inputFile   输入的 WebP 文件
         * @param outputFile  输出的文件（可以是 WebP 或其他格式）
         * @param scaleFactor 缩放比例（例如：0.5 表示将图像缩小一半）
         * @throws IOException 如果读取或写入文件时出现错误
         */
        public static void processWebP(File inputFile, File outputFile, double scaleFactor) throws IOException {
            // 使用 ImageIO 读取 WebP 文件
            ImageInputStream inputStream = ImageIO.createImageInputStream(inputFile);
            Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream);
            if (!readers.hasNext()) {
                throw new IOException("No reader found for WebP format");
            }

            // 选择 WebP 图像读取器
            ImageReader reader = readers.next();
            reader.setInput(inputStream);

            // 读取图像的第一帧
            BufferedImage originalImage = reader.read(0);

            // 计算新的图像大小
            int newWidth = (int) (originalImage.getWidth() * scaleFactor);
            int newHeight = (int) (originalImage.getHeight() * scaleFactor);
            Image resizedImage = originalImage.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);

            // 将缩放后的图像绘制到新的 BufferedImage 中
            BufferedImage resizedBufferedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = resizedBufferedImage.createGraphics();
            g2d.drawImage(resizedImage, 0, 0, null);
            g2d.dispose();

            // 获取 WebP 图像写入器
            ImageWriter writer = ImageIO.getImageWritersByFormatName("webp").next();

            // 设置输出流
            try (ImageOutputStream outputStream = ImageIO.createImageOutputStream(outputFile)) {
                writer.setOutput(outputStream);
                writer.write(resizedBufferedImage);
            }
        }

        /**
         * 压缩 JPEG 图片
         *
         * @param inputFile
         * @param outputFile
         * @param quality
         * @throws IOException
         */
        public static void compressJpeg(File inputFile, File outputFile, float quality) throws IOException {
            BufferedImage image = ImageIO.read(inputFile);

            // Write to output file with specified quality
            ImageOutputStream ios = ImageIO.createImageOutputStream(outputFile);
            ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();

            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(quality); // 0.0f - 1.0f

            writer.setOutput(ios);
            IIOImage iioImage = new IIOImage(image, null, null);
            writer.write(null, iioImage, param);
            ios.close();
            writer.dispose();
        }

        /**
         * 压缩 PNG 图片并保存为新文件
         *
         * @param inputFile  输入的 PNG 图片文件
         * @param outputFile 输出的压缩后的 PNG 图片文件
         * @throws IOException 如果读取或写入文件时出现错误
         */
        public static void compressPng(File inputFile, File outputFile, float quality) throws IOException {
            BufferedImage image = ImageIO.read(inputFile);
            // 使用 TwelveMonkeys 提供的 ImageWriter 对象来压缩 PNG 图片
            ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(quality);  // 设置压缩质量，0.0 - 1.0，1.0 为最佳质量
            try (ImageOutputStream ios = ImageIO.createImageOutputStream(outputFile)) {
                writer.setOutput(ios);
                writer.write(null, new IIOImage(image, null, null), param);
            }
        }

        /**
         * 处理 GIF 图像，设置每帧的延迟时间，并保存为新的 GIF 文件
         *
         * @param inputFile  输入的 GIF 文件
         * @param outputFile 输出的 GIF 文件
         * @param delayTime  毫秒为单位的帧间延迟时间
         * @throws IOException 如果读取或写入文件时出现错误
         */
        public static void setGIFDelayTime(File inputFile, File outputFile, int delayTime) throws IOException {
            // 使用 ImageIO 读取 GIF 文件
            ImageInputStream inputStream = ImageIO.createImageInputStream(inputFile);
            Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream);
            if (!readers.hasNext()) {
                throw new IOException("No reader found for GIF format");
            }

            // 选择 GIF 图像读取器
            ImageReader reader = readers.next();
            reader.setInput(inputStream);

            // 获取 GIF 图像的帧数（GIF 动画的帧数）
            int frameCount = reader.getNumImages(true);

            // 创建图像写入器
            ImageWriter writer = ImageIO.getImageWritersByFormatName("gif").next();

            // 设置输出流
            try (ImageOutputStream outputStream = ImageIO.createImageOutputStream(outputFile)) {
                writer.setOutput(outputStream);

                // 遍历所有帧并设置延迟时间
                for (int i = 0; i < frameCount; i++) {
                    BufferedImage frame = (BufferedImage) reader.read(i);

                    // 设置帧延迟时间
                    ImageWriteParam writeParam = writer.getDefaultWriteParam();
                    writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                    writeParam.setCompressionQuality(0.8f);  // 压缩质量，0.0 - 1.0

                    // 写入每帧，设置每帧的延迟时间
                    javax.imageio.IIOImage iioImage = new javax.imageio.IIOImage(frame, null, null);
                    writer.write(null, iioImage, writeParam);
                }
            }
        }

        /**
         * 读取 WebP 图像并输出到指定文件（支持其他格式，如 PNG）
         *
         * @param inputFile  输入的 WebP 图像
         * @param outputFile 输出的文件
         * @throws IOException 如果读取或写入文件时出现错误
         */
        public static void convertWebPToOtherFormat(File inputFile, File outputFile) throws IOException {
            // 使用 ImageIO 读取 WebP 图像
            ImageInputStream inputStream = ImageIO.createImageInputStream(inputFile);
            Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream);
            if (!readers.hasNext()) {
                throw new IOException("No reader found for WebP format");
            }

            // 选择 WebP 图像读取器
            ImageReader reader = readers.next();
            reader.setInput(inputStream);

            // 读取 WebP 图像的第一帧
            BufferedImage image = reader.read(0);

            // 获取 ImageWriter 来写入其他格式（例如 PNG）
            ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();

            // 设置输出流
            try (ImageOutputStream outputStream = ImageIO.createImageOutputStream(outputFile)) {
                writer.setOutput(outputStream);
                writer.write(image);
            }
        }

        /**
         * 添加文本水印到图像
         *
         * @param inputFile     输入的图像文件
         * @param outputFile    输出的图像文件
         * @param watermarkText 水印文本
         * @throws IOException 如果读取或写入文件时出现错误
         */
        public static void addTextWatermark(File inputFile, File outputFile, String watermarkText) throws IOException {
            // 使用 TwelveMonkeys 读取图像
            ImageInputStream inputStream = ImageIO.createImageInputStream(inputFile);
            Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream);
            if (!readers.hasNext()) {
                throw new IOException("No reader found for the image format");
            }

            ImageReader reader = readers.next();
            reader.setInput(inputStream);

            // 读取图像
            BufferedImage image = reader.read(0);

            // 创建 Graphics2D 对象，用于绘制水印
            Graphics2D graphics = (Graphics2D) image.getGraphics();
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            // 设置水印的字体和颜色
            Font font = new Font("Arial", Font.BOLD, 36);
            graphics.setFont(font);
            graphics.setColor(new Color(254, 83, 83, 128));  // 红色透明水印

            // 获取图像的宽度和高度
            int width = image.getWidth();
            int height = image.getHeight();

            // 计算水印的位置（右下角）
            int x = width - 100;  // 水印的横坐标
            int y = height - 50;  // 水印的纵坐标

            // 绘制文本水印
            graphics.drawString(watermarkText, x, y);

            // 释放资源
            graphics.dispose();

            // 获取 ImageWriter 来写入图像（这里使用 PNG 格式）
            ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();
            try (ImageOutputStream outputStream = ImageIO.createImageOutputStream(outputFile)) {
                writer.setOutput(outputStream);
                writer.write(image);
            }
        }

        /**
         * 添加图像水印
         *
         * @param inputFile      输入的图像文件
         * @param outputFile     输出的图像文件
         * @param watermarkImage 水印图像文件
         * @throws IOException 如果读取或写入文件时出现错误
         */
        public static void addImageWatermark(File inputFile, File outputFile, File watermarkImage) throws IOException {
            // 使用 TwelveMonkeys 读取原始图像
            ImageInputStream inputStream = ImageIO.createImageInputStream(inputFile);
            Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream);
            if (!readers.hasNext()) {
                throw new IOException("No reader found for the image format");
            }

            ImageReader reader = readers.next();
            reader.setInput(inputStream);

            // 读取原始图像
            BufferedImage originalImage = reader.read(0);

            // 读取水印图像
            BufferedImage watermark = ImageIO.read(watermarkImage);

            // 获取 Graphics2D 对象
            Graphics2D graphics = (Graphics2D) originalImage.getGraphics();
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            // 设置水印透明度
            AlphaComposite alphaChannel = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
            graphics.setComposite(alphaChannel);

            // 计算水印图像的位置（右下角）
            int x = originalImage.getWidth() - watermark.getWidth() - 10;
            int y = originalImage.getHeight() - watermark.getHeight() - 10;

            // 绘制水印
            graphics.drawImage(watermark, x, y, null);
            graphics.dispose();

            // 获取 ImageWriter 来写入图像（这里使用 PNG 格式）
            ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();
            try (ImageOutputStream outputStream = ImageIO.createImageOutputStream(outputFile)) {
                writer.setOutput(outputStream);
                writer.write(originalImage);
            }
        }

        // 根据文件扩展名选择合适的压缩方法
        public static void compressImage(File inputFile, File outputFile, String format, Object... params) throws Exception {
            switch (format.toLowerCase()) {
                case "jpg":
                case "jpeg":
                    if (params.length != 1) {
                        throw new IllegalArgumentException("JPEG压缩缺少压缩质量，质量 0 ~ 0.1");
                    }
                    compressJpeg(inputFile, outputFile, (Float) params[0]);
                    break;
                case "png":
                    if (params.length != 1) {
                        throw new IllegalArgumentException("PNG压缩缺少压缩质量，质量 0 ~ 0.1");
                    }
                    compressPng(inputFile, outputFile, (Float) params[0]);
                    break;
                case "gif":
                    if (params.length != 1) {
                        throw new IllegalArgumentException("GIF压缩缺少最大颜色数 上限256");
                    }
                    setGIFDelayTime(inputFile, outputFile, (Integer) params[0]);
                    break;
                case "webp":
                    if (params.length != 1) {
                        throw new IllegalArgumentException("WebP压缩缺少压缩质量，质量 0 ~ 0.1");
                    }
                    convertWebPToOtherFormat(inputFile, outputFile);
                    break;
                default:
                    throw new IllegalArgumentException("Unsupported image format: " + format);
            }
        }

    }
}